package com.quectel.util.aspect.log;

import com.quectel.util.aspect.log.annotation.Log;
import com.quectel.util.common.SpringExpressionParser;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;

/**
 * 拦截Lock注解修饰的方法
 *
 * @author caolu
 * @email louis.cao@quectel.com
 * @date 2023-11-28 14:06:00
 */
public class LogMethodInterceptor implements MethodInterceptor {

    /**
     * 获取方法参数列表中参数真正的名称
     */
    private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();

    private LogCallback logCallback;

    public LogMethodInterceptor(LogCallback logCallback) {
        this.logCallback = logCallback;
    }

    @Nullable
    @Override
    public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {

        Method method = invocation.getMethod();
        Log log = method.getAnnotation(Log.class);


        LogDetail logDetail = new LogDetail();
        logDetail.setMethod(method);
        logDetail.setStartTime(new Date());

        String logValue = log.express();
        if (logValue != null && logValue.contains("#")) {
            Object[] args = invocation.getArguments();
            String[] parameterNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
            if (args != null && parameterNames != null) {
                HashMap<String, Object> elParams = new HashMap<>();
                for (int i = 0; i < parameterNames.length; i++) {
                    elParams.put(parameterNames[i], args[i]);
                }
                logValue = SpringExpressionParser.getValue(elParams, logValue, String.class);
            }
        }
        logDetail.setLog(logValue);
        logDetail.setMethodExecuteFailed(false);
        try {
            return invocation.proceed();
        } catch (Exception e) {
            logDetail.setMethodExecuteFailed(true);
            throw e;
        } finally {
            logDetail.setEndTime(new Date());
            logCallback.callback(logDetail);
        }
    }

}
